﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Bricks.Validation;
using Microsoft.Practices.Unity.InterceptionExtension;
using Val = Bricks.Validation.Validation;

namespace Bricks.Unity.Validation
{

    /// <summary>
    /// 
    /// </summary>
    public class DataErrorInfoBehavior : IInterceptionBehavior
    {

        /// <summary>
        /// 
        /// </summary>
        private static readonly MethodInfo _errorGetMethodInfo = typeof(IDataErrorInfo).GetProperty("Error").GetGetMethod();

        /// <summary>
        /// 
        /// </summary>
        private static readonly MethodInfo _itemGetMethodInfo = typeof(IDataErrorInfo).GetProperty("Item").GetGetMethod();


        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return new[] { typeof(IDataErrorInfo) };
        }

        /// <summary>
        /// 
        /// </summary>
        public bool WillExecute
        {
            get { return true; }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <param name="getNext"></param>
        /// <returns></returns>
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            IMethodReturn methodReturn = getNext.Invoke().Invoke(input, getNext);

            if (input.MethodBase == _errorGetMethodInfo)
            {
                methodReturn.Exception = null;

                methodReturn.ReturnValue = GetError(input.Target, string.Empty);
            }
            else if (input.MethodBase == _itemGetMethodInfo)
            {
                methodReturn.Exception = null;

                string propertyName = input.Arguments.Count == 1 ? input.Arguments[0] as string : string.Empty;

                methodReturn.ReturnValue = GetError(input.Target, propertyName);
            }

            return methodReturn;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="target"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        private string GetError(object target, string propertyName)
        {
            IEnumerable<ValidationResult> validationResults = Val.Validate(target);

            if (validationResults.IsValid())
            {
                return string.Empty;
            }
            else
            {
                return string.Join("\n", validationResults.Where(x => x.Key == propertyName || string.IsNullOrEmpty(propertyName)).Select(x => x.Message)); 
            }
        }
    }
}
